﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Velocity4Net.Ctx;
using Velocity4Net.Runtime.Parse.AST;
using System.IO;

namespace Velocity4Net.Runtime.Directives
{
    /**
     * BlockMacro directive is used to invoke Velocity macros with normal parameters and a macro body.
     * <p>
     * The macro can then refer to the passed body AST. This directive can be used as a 
     * "decorator". Body AST can contain any valid Velocity syntax.
     *
     * An example:
     * <pre>
     * #set($foobar = "yeah!")
     * 
     * #macro(strong $txt)
     * &lt;strong&gt;$bodyContent&lt;/strong&gt; $txt
     * #end
     *
     * #@strong($foobar)
     * &lt;u&gt;This text is underlined and bold&lt;/u&gt;
     * #end
     * </pre>
     * Will print:
     * <pre>
     * &lt;strong&gt;&lt;u&gt;This text is underlined and bold&lt;u&gt;&lt;/strong&gt; yeah!
     * </pre>
     * 
     * bodyContent reference name is configurable (see velocity.properties).
     *
     * @author <a href="mailto:wyla@removethis.sci.fi">Jarkko Viinamaki</a>
     * @since 1.7
     * @version $Id$
     */
    public class BlockMacro : Block
    {
        private String name;
        private RuntimeMacro macro;

        public BlockMacro(String name)
        {
            this.name = name;
        }

        public override String Name
        {
            get
            {
                return key;
            }
            set { }
        }

        /**
         * Override to use the macro name, since it is within an
         * #@myMacro() ... #end block that the scope in question
         * would be used.
         */
        public String getScopeName()
        {
            return name;
        }

        /**
         * Initializes the directive.
         * 
         * @param rs
         * @param context
         * @param node
         * @throws TemplateInitException
         */
        public override void Init(RuntimeServices rs, InternalContextAdapter context, INode node)
        {
            base.Init(rs, context, node);

            // get name of the reference that refers to AST block passed to block macro call
            key = rsvc.GetString(RuntimeConstants.VM_BODY_REFERENCE, "bodyContent");

            // use the macro max depth for bodyContent max depth as well
            maxDepth = rs.GetInt(RuntimeConstants.VM_MAX_DEPTH);

            macro = new RuntimeMacro(name);
            macro.setLocation(Line, Column, TemplateName);
            macro.Init(rs, context, node);
        }

        /**
         * Renders content using the selected macro and the passed AST body.
         *
         * @param context
         * @param writer
         * @param node
         * @return True if the directive rendered successfully.
         * @throws IOException
         */
        public override bool Render(InternalContextAdapter context, TextWriter writer, INode node)
        {
            return macro.render(context, writer, node, new Reference(context, this));
        }

    }

}
